Expand description
An easy to use library for pretty print tables of Rust struct
s and enum
s.
The library supports different approaches of table building.
You can use Tabled
trait if the data type is known.
Or you can use Builder
to construct the table from scratch.
Usage
If you want to build a table for your custom type.
A starting point is to a anotate your type with #[derive(Tabled)]
.
Then to provide your collection to Table::new
and you will be set to render table.
use tabled::{Tabled, Table};
#[derive(Tabled)]
struct Language {
name: &'static str,
designed_by: &'static str,
invented_year: usize,
}
let languages = vec![
Language{
name: "C",
designed_by: "Dennis Ritchie",
invented_year: 1972
},
Language{
name: "Rust",
designed_by: "Graydon Hoare",
invented_year: 2010
},
Language{
name: "Go",
designed_by: "Rob Pike",
invented_year: 2009
},
];
let table = Table::new(languages).to_string();
let expected = "+------+----------------+---------------+\n\
| name | designed_by | invented_year |\n\
+------+----------------+---------------+\n\
| C | Dennis Ritchie | 1972 |\n\
+------+----------------+---------------+\n\
| Rust | Graydon Hoare | 2010 |\n\
+------+----------------+---------------+\n\
| Go | Rob Pike | 2009 |\n\
+------+----------------+---------------+";
assert_eq!(table, expected);
Not all types can derive Tabled
trait though.
The example below can’t be compiled.
#[derive(Tabled)]
struct SomeType {
field1: SomeOtherType,
}
struct SomeOtherType;
Because tabled
must know what we’re up to print as a field, so
each (almoust) field must implement std::fmt::Display
.
Default implementations
Table
can be build from vast majority of Rust’s standard types.
This allows you to run the following code.
use tabled::{Tabled, Table};
let table = Table::new(&[1, 2, 3]);
Combination of types via tuples
Personally I consider this a feature which drives the library to shine.
You can combine any types that implements Tabled
trait into one table.
You can also see in this example a #[header("name")]
usage which configures a header
of a table which will be printed.
You could change it dynamically as well.
use tabled::{
Tabled, Table,
settings::{Style, Alignment, Modify, object::{Rows, Columns, Object}}
};
#[derive(Tabled)]
enum Domain {
Security,
Embedded,
Frontend,
Unknown,
}
#[derive(Tabled)]
struct Developer(#[tabled(rename = "name")] &'static str);
let data = vec![
(Developer("Terri Kshlerin"), Domain::Embedded),
(Developer("Catalina Dicki"), Domain::Security),
(Developer("Jennie Schmeler"), Domain::Frontend),
(Developer("Maxim Zhiburt"), Domain::Unknown),
];
let table = Table::new(data)
.with(Style::psql())
.with(Modify::new(Rows::new(1..).not(Columns::first())).with(Alignment::center()))
.to_string();
assert_eq!(
table,
concat!(
" name | Security | Embedded | Frontend | Unknown \n",
"-----------------+----------+----------+----------+---------\n",
" Terri Kshlerin | | + | | \n",
" Catalina Dicki | + | | | \n",
" Jennie Schmeler | | | + | \n",
" Maxim Zhiburt | | | | + "
)
);
Dynamic table
When you data scheme is not known at compile time.
You most likely will not able to relay on Tabled
trait.
So one option would be is to use Builder
.
use tabled::{builder::Builder, settings::{Modify, object::Rows, Alignment, Style}};
let header = std::iter::once(String::from("i")).chain((0..10).map(|i| i.to_string()));
let mut builder = Builder::default();
builder.set_header(header);
for i in 0..3 {
let mut row = vec![];
row.push(i.to_string());
for j in 0..10 {
row.push((i*j).to_string());
}
builder.push_record(row);
}
let table = builder.build()
.with(Style::rounded())
.with(Modify::new(Rows::new(1..)).with(Alignment::left()))
.to_string();
assert_eq!(
table,
concat!(
"╭───┬───┬───┬───┬───┬───┬────┬────┬────┬────┬────╮\n",
"│ i │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │\n",
"├───┼───┼───┼───┼───┼───┼────┼────┼────┼────┼────┤\n",
"│ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │\n",
"│ 1 │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │\n",
"│ 2 │ 0 │ 2 │ 4 │ 6 │ 8 │ 10 │ 12 │ 14 │ 16 │ 18 │\n",
"╰───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────╯",
)
);
Build table using row!
and col!
macros.
use tabled::{row, col};
let table = row![
col!["Hello", "World", "!"],
col!["Hello"; 3],
col!["World"; 3],
].to_string();
assert_eq!(
table,
concat!(
"+-----------+-----------+-----------+\n",
"| +-------+ | +-------+ | +-------+ |\n",
"| | Hello | | | Hello | | | World | |\n",
"| +-------+ | +-------+ | +-------+ |\n",
"| | World | | | Hello | | | World | |\n",
"| +-------+ | +-------+ | +-------+ |\n",
"| | ! | | | Hello | | | World | |\n",
"| +-------+ | +-------+ | +-------+ |\n",
"+-----------+-----------+-----------+",
)
);
Advanced
Alloc
Table
keeps data buffered, which sometimes not ideal choise.
For such reason there is IterTable
.
It reuses a given data and does not make copies of it ’1.
But because of that it has some limitations compared to Table
It also can be printed directly to io::Write
to not have any intermidiaries.
use tabled::tables::IterTable;
let iterator = (0..3).map(|row| (0..4).map(move |col| format!("{}-{}", row, col)));
let table = IterTable::new(iterator).to_string();
assert_eq!(
table,
"+-----+-----+-----+-----+\n\
| 0-0 | 0-1 | 0-2 | 0-3 |\n\
+-----+-----+-----+-----+\n\
| 1-0 | 1-1 | 1-2 | 1-3 |\n\
+-----+-----+-----+-----+\n\
| 2-0 | 2-1 | 2-2 | 2-3 |\n\
+-----+-----+-----+-----+",
);
’1. It does not make any allocations in case you provide it with width
and count_rows
.
Alloc free
More information
You can find more examples of settings and attributes in README.md
Re-exports
pub use crate::tables::Table;
Modules
- builder
std
- Module is responsible for tables underlyign grid.
- macros
macros
This module contains macro functions for dynamicTable
construction. - Module contains various table configuration settings.
- Module contains a list of table representatives.
Macros
Traits
- Tabled
std
Tabled a trait responsible for providing a header fields and a row fields.